package utils

import (
	"fmt"
	"math/big"
)

type BaseXXConf struct {
	Alphabet           string
	Alphabet_idx0      byte
	Alphabet_bytes_map [256]byte
	BigRadix           *big.Int
	BigZero            *big.Int
	ResultLen          int
}

func MakeAlphabetMapBytes(alphabet string) (rval [256]byte) {
	/**
	  填充255
	*/
	l := len(rval)
	for i := 0; i <= l-1; i++ {
		rval[i] = 255
	}

	alpBuf := []byte(alphabet)

	for i := 0; i <= len(alpBuf)-1; i++ {
		rval[alpBuf[i]] = byte(i)
	}
	return
}

func baseXXEncode2(conf *BaseXXConf, b []byte) string {
	l := len(b)*conf.ResultLen/10000 + 1

	rval := make([]byte, 0, l)
	remain := 0
	for i := 0; i < len(b); i++ {
		v := remain<<8 + int(b[i])
		idx := v % 58
		remain = v / 58
		rval = append(rval, byte(idx))
	}
	if remain > 0 {
		rval = append(rval, conf.Alphabet[remain])
	}

	ReverseBytes(rval)

	return string(rval)
	//while(index < strlen(plainText) ) {
	//	int each = plainText[index];
	//	for(i = len-1; ;i--) {
	//		each += encryption[i] * 256;
	//		encryption[i] = each % 58;
	//		each /= 58;
	//		if(0 == each)
	//		break;
	//	}
	//
	//	i = 0;//输出
	//	while(!encryption[i])
	//	i++;
	//	for(;i <= len-1; i++) {
	//		printf("%d ",encryption[i]);
	//	}
	//	cout << endl;
	//	index++;
	//}

}

func baseXXEncode(conf *BaseXXConf, b []byte) string {
	x := new(big.Int)
	//将b解释为大端存储
	x.SetBytes(b)

	// log2(58) 求就是2的多少次方=58
	//l := float64(len(b)) * conf.ResultLen
	// int(math.Ceil(l))

	answer := make([]byte, 0, len(b)*conf.ResultLen/10000+1) //  int(math.Ceil(l))

	for x.Cmp(conf.BigZero) > 0 {
		mod := new(big.Int)
		//x除于58的余数mod，并将商赋值给x
		x.DivMod(x, conf.BigRadix, mod)
		answer = append(answer, conf.Alphabet[mod.Int64()])
	}

	// leading zero bytes
	//因为如果高位为0，0除任何数为0，可以直接设置为‘1’
	for _, i := range b {
		if i != 0 {
			break
		}
		answer = append(answer, conf.Alphabet_idx0)
	}

	// reverse
	//因为之前先附加低位的，后附加高位的，所以需要翻转
	ReverseBytes(answer)

	return string(answer)
}

func baseXXDecode(conf *BaseXXConf, b string) ([]byte, error) {
	answer := big.NewInt(0)
	j := big.NewInt(1)

	scratch := new(big.Int)
	for i := len(b) - 1; i >= 0; i-- {
		//字符，ascii码表的简版-->得到字符代表的值(0，1,2，..57)
		tmp := conf.Alphabet_bytes_map[b[i]]
		//出现不该出现的字符
		if tmp == 255 {
			return nil, fmt.Errorf("非法的字符串")
		}
		scratch.SetInt64(int64(tmp))

		//scratch = j*scratch
		scratch.Mul(j, scratch)

		answer.Add(answer, scratch)
		j.Mul(j, conf.BigRadix)
	}

	//得到大端的字节序
	tmpval := answer.Bytes()

	var numZeros int
	for numZeros = 0; numZeros < len(b); numZeros++ {
		//得到高位0的位数
		if b[numZeros] != conf.Alphabet_idx0 {
			break
		}
	}
	//得到原来数字的长度
	flen := numZeros + len(tmpval)
	//构造一个新地存放结果的空间
	val := make([]byte, flen, flen)
	copy(val[numZeros:], tmpval)

	return val, nil
}
